﻿using MRSite.Cache;
using MRSite.Cache.Configuration;
using MRSite.Common.Dependency;
using MRSite.DataAccess.Config;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
using System.Threading.Tasks;

namespace MRSite.DataAccess
{
    public class ConnectionStringManager : IConnectionStringManager, ISingleton
    {
        public ConnectionStringManager(ICacheConfiguration cacheConfiguration, ICacheManager cacheManager)
        {
            _cacheConfiguration = cacheConfiguration;
            _cacheManager = cacheManager;
        }

        private static ProviderType ConvertProviderType(string providerName, string databaseName, string connStr)
        {
            if (string.IsNullOrWhiteSpace(providerName))
            {
                return ProviderType.SqlServer;
            }
            var name = providerName.Trim().ToLower();
            switch (name)
            {
                case "sqlserver":
                    return ProviderType.SqlServer;
                case "odbc":
                    return ProviderType.Odbc;
                case "oledb":
                    return ProviderType.OleDb;
                default:
                    throw new ConfigurationErrorsException("Not support this database provider '" + providerName + "' for database whose name is '" + databaseName
                        + "' and connection string is '" + connStr + "'.");
            }
        }

        public DatabaseInstance GetDatabaseInstance(string name)
        {
            var dbList = GetDatabaseList();
            return dbList.Find(x => x.Name == name);
        }

        private List<DatabaseInstance> GetDatabaseList()
        {
            if (ConfigHelper.DatabaseListFilePath == null || ConfigHelper.DatabaseListFilePath.Trim().Length <= 0
                    || File.Exists(ConfigHelper.DatabaseListFilePath.Trim()) == false)
            {
                return null;
            }
            _cacheConfiguration.ConfigureOneCache(typeof(ConnectionStringManager).FullName, c =>
            {
                c.ChangeMonitors.Add(new HostFileChangeMonitor(new string[] { ConfigHelper.DatabaseListFilePath.Trim() }));
            });
            return _cacheManager.GetCache(typeof(ConnectionStringManager).FullName).Get("GetDatabaseList",
                () =>
                {
                    var list = ConfigHelper.LoadDatabaseListFile();
                    if (list != null && list.DatabaseInstances != null && list.DatabaseInstances.Length > 0)
                    {
                        List<DatabaseInstance> rst = new List<DatabaseInstance>(list.DatabaseInstances.Length);
                        foreach (var db in list.DatabaseInstances)
                        {
                            if (db != null && string.IsNullOrWhiteSpace(db.Name) == false
                                && string.IsNullOrWhiteSpace(db.ConnectionString) == false)
                            {
                                if (rst.Exists(x => x.Name == db.Name.Trim()))
                                {
                                    throw new ApplicationException("Duplidated database name '" + db.Name + "' in configuration file '" + ConfigHelper.DatabaseListFilePath + "'.");
                                }
                                rst.Add(new DatabaseInstance()
                                {
                                    Name = db.Name.Trim(),
                                    ConnectionString = db.ConnectionString,//Decrypt(db.ConnectionString),
                                    Type = db.Type
                                });
                            }
                        }
                        return rst;
                    }
                    return null;
                });
        }
        private readonly ICacheConfiguration _cacheConfiguration;
        private readonly ICacheManager _cacheManager;

        internal class ConnStrInfo
        {
            private string _name;
            private string _connStr;
            private ProviderType _providerType;

            public ConnStrInfo(string name, string connStr, string type)
                : this(name, connStr, ConvertProviderType(type, name, connStr))
            {

            }

            public ConnStrInfo(string name, string connStr, ProviderType providerType)
            {
                _name = name;
                _connStr = connStr;
                _providerType = providerType;
            }

            public string Name { get; }

            public string ConnStr { get; }

            public ProviderType ProviderType { get; }
        }
    }
}
